package function;

import org.dom4j.*;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class InsertDataIntoTable {

    //insert into 表名(列名称1，列名称2) values(列值1，列值2);
    public static void insertIntoTable(String dbName, String tbname, List<String> tmp1, List<String> tmp2) throws DocumentException, IOException {
        //数据库是否合法
        if (!IsLegal.isDatabase()) {
            return;
        }

        //表存在则打开配置文件
        File config_file = IsLegal.isTable(dbName, tbname);
        //解析配置文件
        SAXReader config_file_reader = new SAXReader();
        Document config_file_document = config_file_reader.read(config_file);
        Element table_message = config_file_document.getRootElement();
        Element write_file_element = (Element) config_file_document.getRootElement().selectSingleNode(tbname);
        Element insertable_element = (Element) config_file_document.getRootElement().selectSingleNode("insertables");

        // 进行类型判断
        if (typeLaw(tbname, tmp1, tmp2, table_message)) return;

        //  table_att  储存该表的所有属性
        List<String> table_att = new ArrayList<>();
        List<Attribute> attributes = table_message.attributes();
        for (Attribute attribute : attributes) {
            table_att.add(attribute.getName());
        }

        Map<String, String> tableMap = getMapFromTable(table_att, tmp1, tmp2);

        //判断是否存在索引，存在则插入数据后更新索引
        boolean need_updateIndex = IsLegal.hasIndex(dbName, tbname);
        //若有物理层可插入子表，则将数据插入到该物理层子表
        String write_file_name;
        if (insertable_element.selectNodes("insertable").size() > 0) {
            write_file_name = tbname + insertable_element.selectSingleNode("insertable").getText();
            //创建写入对象，获取记录数量
            File write_file = new File("./mydatabase/" + dbName + "/" + tbname + "/" + write_file_name + ".xml");
            SAXReader saxReader = new SAXReader();
            Document document = saxReader.read(write_file);
            Element rootElement = document.getRootElement();
            Element element = rootElement.addElement(tbname);


            insertToElement(element, tableMap);
            //若可插入文件记录数>=10,更新配置信息
            List<Node> nodes = document.getRootElement().selectNodes(tbname);
            if (nodes.size() >= CreateTable.entry_num) {
                insertable_element.remove(insertable_element.selectSingleNode("insertable"));
                CreateTable.writeIO(config_file, config_file_document);
            }
            CreateTable.writeIO(write_file, document);

            System.out.println("插入成功");
            //插入数据后更新索引
            if (need_updateIndex) {
                CreateIndex.updateIndex_insert(tbname, tmp2.get(1), write_file_name);
            }


        } else {


            //否则插入数据到物理层最后一张子表
            write_file_name = tbname + write_file_element.getText();
            //创建写入对象，获取记录数量
            File write_file = new File("./mydatabase/" + dbName + "/" + tbname + "/" + write_file_name + ".xml");
            SAXReader saxReader = new SAXReader();
            Document document = saxReader.read(write_file);
            Element rootElement = document.getRootElement();

            List<Node> nodes = rootElement.selectNodes(tbname);


            //如果该文件中记录个数>=10，新建文件写入
            if (nodes.size() >= CreateTable.entry_num) {
                Document newDocument = DocumentHelper.createDocument();
                Element newRoot = newDocument.addElement(tbname + "s");
                Element newChild = newRoot.addElement(tbname);
                //更新文件数量
                int file_amount_int = (Integer.parseInt(write_file_element.getText()) + 1);
                String file_amount = ("" + file_amount_int);
                //设置新文件名
                String newfile = tbname + file_amount;

                insertToElement(newChild, tableMap);
                CreateTable.writeIO(new File("./mydatabase/" + dbName + "/" + tbname + "/" + newfile + ".xml"), newDocument);

                System.out.println("插入成功");
                //更新配置文件中表的文件数量
                write_file_element.setText(file_amount);
                CreateTable.writeIO(config_file, config_file_document);
                //插入数据后更新索引
                if (need_updateIndex) {
                    CreateIndex.updateIndex_insert(tbname, tmp2.get(1), write_file_name);
                }
            }
            //否则在最后一条记录后插入
            else {
                //创建新节点
                Element childElement = rootElement.addElement(tbname);
                insertToElement(childElement, tableMap);

                CreateTable.writeIO(write_file, document);
                System.out.println("插入成功");
                //插入数据后更新索引
                if (need_updateIndex) {
                    CreateIndex.updateIndex_insert(tbname, tmp2.get(1), write_file_name);
                }
            }

        }

    }


    private static boolean typeLaw(String tbname, List<String> tmp1, List<String> tmp2, Element rootElement) {


        for (int i = 1; i < tmp1.size(); i++) {
            String columnName = tmp1.get(i); // 列名
            String columnValue = tmp2.get(i); // 值


            // 获取列的类型（从根元素的属性中获取）
            String dataType = rootElement.attributeValue(columnName);

            // 检查数据类型是否有效
            if (dataType == null) {
                System.out.println("插入失败：列 " + columnName + " 不存在于表 " + tbname);
                return true;
            }

            if (!IsValidType.isValidType(columnValue, dataType)) {
                System.out.println("插入失败：列 " + columnName + " 的值 " + columnValue + " 类型不匹配，应为 " + dataType);
                return true;
            }
        }
        return false;
    }


    //根据表中的列,生成映射的键值对,没有输入的为"NULL"
    private static Map<String, String> getMapFromTable(List<String> table_att, List<String> tmp1, List<String> tmp2) {
        Map<String, String> result = new HashMap<>();

        for (String att : table_att) {
            if (tmp1.contains(att)) {
                int index = tmp1.indexOf(att);
                result.put(att, tmp2.get(index));
            } else {
                result.put(att, "NULL");
            }
        }

        return result;

    }

    //向表中插入数据,以键值对的形式
    private static void insertToElement(Element element, Map<String, String> map) {
        for (String key : map.keySet()) {
            String value = map.get(key);
            element.addAttribute(key, value);
        }
    }

}
